雙十連假,讓我先用庫存擋擋呀~~~~
這篇主要是紀錄兩個套件的使用,以及碰到的問題,希望對那些被業主有同樣要求的夥伴有個參考XD
開發上常常碰到業主有匯出 PDF 檔案的需求,剛好做到這個功能所以記錄下來
wkhtmltopdf
套件,所以優先嘗試這個套件Snappy 套件
,方便在 Laravel 使用pdftk 套件
官方文件沒有,但因為我是使用 Mac os,所以我是用 homebrew 安裝:
```php
brew install --cask wkhtmltopdf
```
安裝完後輸入 wkhtmltopdf 就會有相關資訊
snappy github 有提到一些 wkhtmltopdf 已知檔案位置的問題,可以檢查一下有沒有安裝到確切位置
Attention vagrant users!
Move the binaries to a path that is not in a synced folder, for example:
cp vendor/h4cc/wkhtmltoimage-amd64/bin/wkhtmltoimage-amd64 /usr/local/bin/
cp vendor/h4cc/wkhtmltopdf-amd64/bin/wkhtmltopdf-amd64 /usr/local/bin/
and make it executable:
chmod +x /usr/local/bin/wkhtmltoimage-amd64
chmod +x /usr/local/bin/wkhtmltopdf-amd64
This will prevent the error 126.
snappy 的 github文件算清楚,建議可以先自己閱讀試試看
在專案根目錄 terminal視窗輸入
composer require barryvdh/laravel-snappy
config/app.php
裡面,找到圖片 ‘providers’
這個區塊,加入 serviceProvider.php
Barryvdh\Snappy\ServiceProvider::class,
然後建立 snappy 的 config 檔案
php artisan vendor:publish --provider="Barryvdh\Snappy\ServiceProvider"
我的 config 檔案裡面有加入一些頁面設定,最後長這樣
'pdf' => [
'enabled' => true,
// 這裡務必要確認 wkhtmltopdf 的安裝位置,要放在這邊
'binary' => env('WKHTML_PDF_BINARY', '/usr/local/bin/wkhtmltopdf'),
'timeout' => false,
'options' => [
'page-size' => 'A4',
'javascript-delay' => 3000,
'encoding' => 'UTF-8', //顯示中文字建議放
'zoom' => 1,
],
'env' => [],
],
'image' => [
'enabled' => true,
'binary' => env('WKHTML_IMG_BINARY', '/usr/local/bin/wkhtmltoimage'),
'timeout' => false,
'options' => [],
'env' => [],
],
使用上不難, github 給了很詳細的示範程式碼:https://github.com/barryvdh/laravel-snappy#usage
inline() 是在瀏覽器視窗顯示、save()是存在專案裡面、download() 是直接下載。
setOption() 函式要參考 wkhtmltopdf 的官方文件,可以寫在 config/snappy.php 或直接在 controller 程式碼當中
這裡用了表頭 header
有些文件會固定在頁面右上角顯示 ID 號碼等等,且每一頁都要顯示的這種功能,以 word 編輯來說會放在 頁首/頁尾,wkhtmltopdf 官方文件有寫到,這種功能要有 頁首html 及內容html 兩個html檔案去渲染
$group = Group::find($groupId);
// 內容 html
$pdf = SnappyPdf::loadView('groupPdf', [
'group' => $group,
]);
// 頁首 html
$header = view('groupHeaderPdf', [
'group' => $group,
]);
// 用 setOption 加入頁首
$pdf->setOption('header-html', $header)
->save(base_path("groups/$fileName.pdf"), true);
wkhtmltopdf 官方文件有 header & footer 說明及一些可用變數
到此應該可以產出PDF檔案了吧~可以先用 download() 下載看看產生的 PDF 是否符合需求。
提供常碰到的問題與方向:(最困難的在這….建議可以開個AWS EC2或其他測試環境,避免把自己電腦搞壞)
上面錯誤可能不會有明確錯誤訊息,所以處理起來超麻煩….
前端提供的 html 畫面節錄
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" type="text/css" **href="storage/assets/css/common.css"/>
//問題出在這裡**
</head>
用 postman 打 api 出現的錯誤訊息
The exit status code '1' says something went wrong:
為了這個錯誤訊息真是搞了一整天,向 mentor 求救:
"message": "The exit status code '1' says something went wrong:\n
stderr: \"Loading pages (1/6)\n
[> ] 0%\r
[======> ] 10%\r
Warning: Blocked access to file \n
Warning: Blocked access to file \n
Warning: Blocked access to file \n
Warning: Blocked access to file \n[
==========> ] 17%\r
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol \"about\" is unknown\n
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol \"about\" is unknown\n
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol \"about\" is unknown\n
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol \"about\" is unknown\n
[=============> ] 23%\r
[================> ] 27%\r
[===================> ] 32%\r
[=======================> ] 39%\r
Warning: Blocked access to file \n
Warning: Blocked access to file \nError: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol \"about\" is unknown\n
Error: Failed to load about:blank, with network status code 301 and http status code 0 - Protocol \"about\" is unknown\n
[============================================================] 100%\rCounting pages (2/6) \n
[============================================================] Object 1 of 1\r
Resolving links (4/6) \n
[============================================================] Object 1 of 1\r
Loading headers and footers (5/6) \n
Printing pages (6/6)\n
[> ] Preparing\r
[===============> ] Page 1 of 4\r
[==============================> ] Page 2 of 4\r
[=============================================> ] Page 3 of 4\r
[============================================================] Page 4 of 4\r
Done \n
Exit with code 1 due to network error: ProtocolUnknownError\n
\"\nstdout: \"\"\n
command: /usr/local/bin/wkhtmltopdf --lowquality '/var/tmp/knp_snappy64f7428490b5b2.29394085.html' '/var/tmp/knp_snappy64f7428490f604.17908708.pdf'.",
"exception": "RuntimeException",
"file": "/Users/mia/Sites/luxgen/luxgen_backed/vendor/knplabs/knp-snappy/src/Knp/Snappy/AbstractGenerator.php",
"line": 469,
從錯誤訊息中可以看到似乎是什麼檔案讀不到,又前端給的 HTML 檔案有幾個 CDN 引用的檔案,稍微測試一下後發現,如果 HTML 只留下純文字,是可以正常顯示的。
因此修改了前端給的 HTML 檔案,把圖片連結檔案跟前端引入的 CDN 都拿掉,文字就出現了!!
但版型還是需要的,後來去確認外部連結的檔案位置、用 helper function 設定 assets()
才成功
<link rel="stylesheet" type="text/css" href="{{ asset('storage/assets/css/common.css') }}"/>
小撇步:可以用 tinker 測試路徑正不正確
當我改完 assets() 後,每次送出請求仍然等很久,最後顯示請求處理時間過長。
後來找到這篇:https://github.com/barryvdh/laravel-snappy/issues/237
意思是我的專案伺服器用 php artisan serve
啟動,php artisan serve 是 Laravel 框架提供的用於在本地運行開發伺服器的命令,方便開發和測試 Laravel 應用程序。這個方式非常簡便,但無法像 Nginx 或 Apache 支援更多複雜的功能,例如在處理這個請求時, php artisan serve 沒辦法同時要外連到指定的檔案 (上面的 assets() 檔案),導致請求超過處理時間。
結論還是要去 AWS 開一個測試機進行測試…然後衍伸出了,因為檔案權限不符合nginx設置,所以報錯的問題。預計要儲存的位置,檔案權限要是www-data
drwxrwxr-x 2 www-data www-data 4096 Sep 18 09:56 .
// 這個存放的資料夾要能讓 nginx 操作
drwxrwxr-x 15 ubuntu ubuntu 4096 Sep 18 09:47 ..
-rw-rw-r-- 1 ubuntu ubuntu 39435 Sep 18 09:47 A0223000003.pdf
-rw-r--r-- 1 www-data www-data 39879 Sep 18 09:56 A0223000006.pdf
這個問題的起因是因為我沒有把 filesystem 的寫法搞懂,導致程式碼無法在指定的資料夾儲存生成的 PDF。有興趣的話可以先看一下官方文件 filesystem 章節,明天會詳細看一下我的檔案到底怎麼拿、從何處下載。
到這邊應該可以正常產出 PDF 檔案了吧XD
密碼設定要用到下一個套件: pdftk,就留在下一篇囉!